Release 10.1A: OpenEdge Development:
Progress 4GL Handbook
Defining triggers
The most basic way to define a trigger is to put the trigger definition directly into the object definition:
The
TRIGGERSblock in aDEFINEstatement can contain one or more individual trigger definitions, each starting with anONphrase naming the event followed by a single statement or aDO-ENDblock of statements. This form is called a definitional trigger.If you use the AppBuilder to create your application procedures, it always creates separate blocks of executable code that attach triggers to objects at run time. This form is called a run-time trigger. There is no inherent advantage to one form over the other. Either way, the code in the trigger block is compiled and turned into r-code along with the rest of the procedure. You should use the AppBuilder to organize your trigger blocks to provide a more readable structure to your procedures.
The form of the trigger block for a run-time trigger names both the event and the object it applies to:
Either the
event-nameor theobject-name(or both) can be a comma-separated list. Thestatementscan be either a single statement or aDO-ENDblock of statements.Setting up triggers to be executed
A trigger is executed if the object it applies to is enabled (sensitive), and if the trigger is currently active and in scope.
The trigger is active if the statement that defines it has been executed. As discussed earlier in the book, Progress processes statements in a procedure in the order it encounters them in. Thus, the definition of an object must come before the block of code that defines a trigger for the object. Otherwise, Progress won’t recognize the object reference. And the trigger definition must come before the user receives control and can perform the action that would fire the event and run the trigger.
The scope of a definitional trigger is the scope of the object it’s defined for. The scope of a run-time trigger is the nearest containing procedure or trigger block where it is defined. So if a trigger definition is inside an internal procedure, then its scope is limited to that internal procedure. If it’s outside of any internal procedure, its scope is the entire external procedure.
The AppBuilder places all trigger definitions toward the top of the procedure, following the Definitions section of the procedure but before the main block and all internal procedures. In this way the trigger blocks are scoped to the entire procedure and they are made active before any user actions that invoke them can occur.
![]()
To build a very simple example procedure to demonstrate some of the rules of run-time trigger processing in Progress:
- Define a button and give it an initial label, then define an
INTEGERvariable as a counter:
- Add a statement to enable the button in a frame. As you learned earlier, this causes both the button and its frame to be instantiated and realized:
- Define a run-time trigger for the button that changes its label so that you can see that the trigger fired:
- Create a
WAIT-FORstatement that blocks the termination of the procedure and allows the user to click the button:
- Run the procedure. As you would expect, the button’s label is changed when you click the button:
![]()
The
ON CHOOSEtrigger you defined is scoped to the entire procedure and executes each time the button is pressed.
![]()
To define another trigger for the button in an internal procedure, make these changes to the procedure:
Before the
WAIT-FORstatement the code runs the internal procedureChooseProc. This procedure defines a different trigger for the same button, which identifies the trigger with the labelInternal. The second trigger definition replaces the first one within theChooseProcprocedure.Note that the scope of the button is the entire external procedure because it is defined at that level. The scope of the variable
iCountis also the external procedure for the same reason. But what is the scope of the second trigger definition you just added?It is scoped only to the internal procedure where it is defined. So what happens when you run this version of the procedure? Before you run it, walk through the code in your head to decide what happens.
The external procedure defines a trigger for the button. It then runs an internal procedure that defines a different trigger. That internal procedure then exits, without giving the user any chance to use the new trigger, and its trigger goes out of scope. So what happens when the user clicks the button?
Figure 8–9: Result of trigger example procedure
![]()
Figure 8–9 shows that Progress reverts to the original trigger. This example shows you that Progress effectively keeps a stack of trigger definitions. If a later definition goes out of scope, Progress reverts to the trigger definition that is now back in scope (if there is one).
So how would you see the effect of the internal procedure trigger? You can place a
WAIT-FORstatement inside the internal procedure to force Progress to wait until the user clicks the button.
![]()
To try this, add this statement to the end of the
ChooseProcprocedure:
Note that you can wait for any event, not just the close of the procedure or its window. This statement waits until the user clicks the button exactly once. Then the
WAIT-FORis satisfied, the internal procedure exits, and the first trigger takes over.
![]()
To see the result of each button press, run the procedure again. Figure 8–10 shows the result of the first four button presses.
Figure 8–10: Results of running the ChooseProc procedure
![]()
Making a trigger persistent
Adding the second
WAIT-FORstatement is very awkward, and it is pretty close to an absolute rule in Progress that your entire application, not just a single procedure, should have only a singleWAIT-FORstatement. This rule is summarized in the saying: “One world, one WAIT-FOR.” MultipleWAIT-FORstatements can easily get tangled up in each other if they are not satisfied in the exact reverse order from the order they are defined in, and this can result in aWAIT-FORthat doesn’t terminate properly.So how else do you establish a trigger inside an internal procedure or a trigger block, or for that matter inside another external procedure that you call, and have that trigger persist beyond the scope of its declaration?
Progress provides a special statement to let you do this:
The persistent trigger definition can have only this one
RUNstatement (not aDO-ENDblock with any other statements). You can pass optionalINPUTparameters to the procedure you run but noOUTPUTorINPUT-OUTPUTparameters. If you pass parameters, the parameter values are evaluated once when the trigger is defined. They are not re-evaluated each time the trigger executes.
![]()
To see how you write a persistent trigger and what its effects are, change the
ChooseProcprocedure and add the new procedurePersistProc, as follows:
Now when you run the procedure and click the button you get a very different result.
Figure 8–11 shows the results of the first three button presses.Figure 8–11: Results of running the PersistProc procedure
![]()
Once you have established the persistent trigger, it remains in effect as long as the object it’s associated with exists, just as a definitional trigger would.
Using the REVERT statement to cancel a trigger
An
ONstatement can contain the single keywordREVERTto cancel an existing trigger definition before it goes out of scope:
The
REVERTstatement cancels any run-time trigger for the event and object defined in the current procedure or trigger. Note that you cannot useREVERTto cancel a persistent trigger. Instead, you must run another persistent trigger procedure that either defines a new trigger or consists of just aRETURNstatement.Defining triggers to fire anywhere
You can use the
ANYWHEREoption of theONstatement to set up a trigger that applies to all objects in an application. Use the following syntax:
|
Copyright © 2005 Progress Software Corporation www.progress.com Voice: (781) 280-4000 Fax: (781) 280-4095 |